#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Mark.h"

#include "Class.h"
#include "OrdColl.h"
#include "Math.h"

//---- Mark --------------------------------------------------------------------

NewMetaImpl(Mark,Object, (T(pos), T(len), T(state)));

Mark::Mark(int p, int l, eMarkState s, eMarkFlags flgs)
{
	pos= p;
	len= l;
	state= s;
	if ((flgs & eMarkInclStart) == eMarkInclStart)
		SetFlag(eMarkInclStart);
	if ((flgs & eMarkFixedSize) == eMarkFixedSize)
		SetFlag(eMarkFixedSize);
	if ((flgs & eMarkLocked) == eMarkLocked)
		SetFlag(eMarkLocked);
}

Mark::Mark(Mark *m)
{
	pos= m->pos;
	len= m->len;
	state= m->state;
}

int Mark::Compare(Object *op)
{
	return (pos - ((Mark*)op)->pos);
}

bool Mark::HasChanged(int start,int l)
{
	return (state != eStateNone || pos != start || len != l);
}

OStream &Mark::PrintOn(OStream &s)
{
	Object::PrintOn(s);
	return s << pos SP << len SP << state SP;
}

IStream &Mark::ReadFrom(IStream &s)
{
	Object::ReadFrom(s);
	return s >> pos >> len >> Enum(state);
}

Object *Mark::deepclone()
{
	if (IsA() == Meta(Mark))
		return new Mark(pos, len, (eMarkState)state);
	return 0;
}

void Mark::Cut(int at, int n)
{
	if (at < 0)
		n+= at;
	at= Math::Max(0, at);

	// the different cases are shown as: '|' = mark positions '^' deleted range

	if (TestFlag(eMarkLocked))
		return;

	if (TestFlag(eMarkFixedSize)) {
		// ^ |    |   ^ includes ^ |   ^|
		if (at <= pos && at + n >= pos + len) {
			state= eStateDeleted;
			len= 0;
			pos= at;
		}
		// ^ ^ |    | includes ^  ^|   |
		else if (at + n <= pos) {
			pos-= n;
		}

		//    |    |  ^  ^ includes |    |^  ^
		else if (at >= pos + len)
			;
		// ^ | ^  |,  |  ^ | ^,| ^ ^ | includes |^   ^|
		else
			Error("Cut", "should not occur");

	} else {
		// | ^ ^ | includes |^   ^|
		if (at >= pos && at + n <= pos + len) { // contained
			state= eStateChanged;
			len-= n;
		}

		// ^ |    |   ^ includes ^ |   ^|
		else if (at < pos && at + n >= pos + len) {
			state= eStateDeleted;
			len= 0;
			pos= at;
		}

		// ^ | ^  |
		else if (at < pos && at + n > pos) {
			len= pos+len - (at +n);
			pos= at;
			state= eStateChanged;
		}

		// |  ^ | ^
		else if (at >= pos && at < pos + len) {
			len= at - pos;
			state= eStateChanged;
		}

		// ^ ^ |    | includes ^  ^|   |
		else if (at + n <= pos)
			pos-= n;

		//    |    |  ^  ^ includes |    |^  ^
		else if (at >= pos + len)
			;
		else
			Error("Cut", "should not occur");
	}
}

void Mark::Paste(int at, int n)
{
	n= Math::Max(0, n);
	at= Math::Max(0, at);

	if (TestFlag(eMarkLocked))
		return;
	if ((at > pos || (TestFlag(eMarkInclStart) && at == pos)) && at < pos + len){
		state= eStateChanged;
		len += n;
		
	} else if (at < pos || (!TestFlag(eMarkInclStart) && at == pos))
		pos += n;
}

void Mark::Change(int at, int n)
{
	if (TestFlag(eMarkLocked))
		return;
	if (at < pos && at+n >= pos+len)
		state= eStateChanged;
	else if (at >= pos && at < pos+len)
		state= eStateChanged;
	else if (at+n >= pos && at+n < pos+len)
		state= eStateChanged;
}

//---- MarkList ----------------------------------------------------------------

NewMetaImpl(MarkList,Object, (TP(marks), TB(doRemove)));

MarkList::MarkList(bool remove, SeqCollection *rep)
{
	doRemove= remove;
	if (rep)
		marks= rep;
	else
		marks= new OrdCollection;
}

MarkList::~MarkList()
{
	if (doRemove)
		marks->FreeAll();
	SafeDelete(marks);
}

void MarkList::Add(Mark *m)
{
	marks->Add(m);
}

Mark *MarkList::Remove(Mark *m)
{
	return (Mark*)marks->Remove(m);
}

Mark *MarkList::At(int i)
{
	return (Mark*)marks->At(i);
}

Iterator *MarkList::MakeIterator(void *placement)
{
	return marks->MakeIterator(cIterForward, placement);
}

void MarkList::FreeAll()
{
	marks->FreeAll();
}

int MarkList::Size()
{
	return marks->Size();
}

void MarkList::Empty()
{
	marks->Empty(marks->Size());
}

void MarkList::Replace(int from, int to, int sz)
{
	Iter next(marks);
	Mark *m;
	int n= to-from;

	while (m= (Mark*)next()) {
		if(from != to) {
			m->Cut(from, n);
			if (doRemove && m->state == eStateDeleted) {
				marks->Remove(m);
				m->FreeAll();
				delete m;
			}
		}
		if (sz)
			m->Paste(from, sz);
	}
}

void MarkList::RangeChanged(int at,int n)
{
	Iter next(marks);
	Mark *m;

	n= Math::Max(0, n);
	at= Math::Max(0, at);

	while (m= (Mark*) next())
		m->Change(at, n);
}

OStream& MarkList::PrintOn(OStream &s)
{
	Object::PrintOn(s);
	return s << doRemove << marks SP;
}

IStream& MarkList::ReadFrom(IStream &s)
{
	Object::ReadFrom(s);
	return s >> Bool(doRemove) >> marks;
}

